#ifdef __aarch64__

.text
.align 5
.global IndirectGemmInt8to32_8x4_dp
#ifndef __APPLE__
.type IndirectGemmInt8to32_8x4_dp, %function
#endif

// void IndirectGemmInt8to32_8x4_dp(int8_t *output, int8_t *input, int8_t *weight, int32_t *bias,
//     size_t kSize, size_t ic4, size_t oc4, size_t offset, int min, int max);
// x0: output, x1: input, x2: weight, x3: bias, x4: kSize, x5: ic4, x6: oc4, x7: offset
// x8: min, x9: max
// BIAS IS TO SUBSTRACT, NOT TO ADD HERE
// we use sdot intrinsic on cores that supports dotprod(Armv8.2-A w/dp or later)
// mrs intrinsic could read system register ID_AA64ISAR0_EL1(or s3_0_c0_c6_0 on Armv8.2-A)
// the 44-48 bits indicates whether dotprod is supported
IndirectGemmInt8to32_8x4_dp:

    .macro INIT_BIAS
        ld1 {v14.4s}, [x3], #16
        dup v15.4s, wzr
        sub v15.16b, v15.16b, v14.16b
        mov v16.16b, v15.16b
        mov v18.16b, v16.16b
        mov v18.16b, v16.16b
        mov v19.16b, v16.16b
        mov v20.16b, v16.16b
        mov v21.16b, v16.16b
        mov v22.16b, v16.16b
        mov v23.16b, v16.16b
        mov v24.16b, v16.16b
        mov v25.16b, v16.16b
        mov v26.16b, v16.16b
        mov v27.16b, v16.16b
        mov v28.16b, v16.16b
        mov v29.16b, v16.16b
        mov v30.16b, v16.16b
        mov v31.16b, v16.16b
    .endm

    // registers v8 ~ v15 must be preserved by a callee across subroutine calls, according to
    // https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#simd-and-floating-point-registers
    // r19 ~ r29 should be also preserved
    // whereas our coding style do not permit such amount of parameters

    ldr w8, [sp, #0]
    ldr w9, [sp, #8]

    mul x5, x4, x5
    mov x4, #1

    LoopStart:

        mov x10, x4
        mov x12, x1

        LoopKw:

            mov x11, x0
            INIT_BIAS

            // as some processors do not support sdot intrinsic, we use instruction word
            // dp support is stilled judged dymaticly, instruction word is just used to ensure compilation
            // according to https://static.docs.arm.com/ddi0596/g/ISA_A64_xml_v86A-2020-03_OPT.pdf
            // the instruction word of sdot vd.4s, vn.16b, vm.4b[index] is
            // 0100 1111 10Lm mmmm 1110 H0nn nnnd dddd
            // mmmmm/nnnnn/ddddd is the number of neon register, HL is the high/low bit of index

            // load input for output 1-8
            ld1 {v0.16b, v1.16b}, [x12], #32
            // load weight
            ld1 {v4.16b}, [x2], #16
            // step for output 1-4
            sdot v16.4s, v4.16b, v0.4b[0]
            sdot v17.4s, v4.16b, v0.4b[1]
            sdot v18.4s, v4.16b, v0.4b[2]
            sdot v19.4s, v4.16b, v0.4b[3]
            // load input for output 9-16
            ld1 {v2.4s, v3.4s}, [x12], #32
            // another step for output 5-8
            sdot v20.4s, v4.16b, v1.4b[0]
            sdot v21.4s, v4.16b, v1.4b[1]
            sdot v22.4s, v4.16b, v1.4b[2]
            sdot v23.4s, v4.16b, v1.4b[3]

            subs x13, x5, #1
            beq LoopIcEnd

            LoopIc:
                // load input for output 1-8
                ld1 {v0.16b, v1.16b}, [x12], #32
                prfm pldl1keep, [x2, #64]
                sdot v24.4s, v4.16b, v2.4b[0]
                sdot v25.4s, v4.16b, v2.4b[1]
                sdot v26.4s, v4.16b, v2.4b[2]
                sdot v27.4s, v4.16b, v2.4b[3]

                sdot v28.4s, v4.16b, v3.4b[0]
                sdot v29.4s, v4.16b, v3.4b[1]
                sdot v30.4s, v4.16b, v3.4b[2]
                sdot v31.4s, v4.16b, v3.4b[3]
                // load weight
                ld1 {v4.16b}, [x2], #16
                // load input for output 9-16
                ld1 {v2.4s, v3.4s}, [x12], #32
                sdot v16.4s, v4.16b, v0.4b[0]
                sdot v17.4s, v4.16b, v0.4b[1]
                sdot v18.4s, v4.16b, v0.4b[2]
                sdot v19.4s, v4.16b, v0.4b[3]
                // another step for output 5-8
                sdot v20.4s, v4.16b, v1.4b[0]
                sdot v21.4s, v4.16b, v1.4b[1]
                sdot v22.4s, v4.16b, v1.4b[2]
                sdot v23.4s, v4.16b, v1.4b[3]

                subs x13, x13, #1
                bne LoopIc

            LoopIcEnd:
                sdot v24.4s, v4.16b, v2.4b[0]
                sdot v25.4s, v4.16b, v2.4b[1]
                sdot v26.4s, v4.16b, v2.4b[2]
                sdot v27.4s, v4.16b, v2.4b[3]

                sdot v28.4s, v4.16b, v3.4b[0]
                sdot v29.4s, v4.16b, v3.4b[1]
                sdot v30.4s, v4.16b, v3.4b[2]
                sdot v31.4s, v4.16b, v3.4b[3]
                // prefetching is not prefered while writing results in spite of cache missings
                // you could try prfm pstl2strm
                dup v0.4s, w8
                fmax v16.4s, v16.4s ,v0.4s
                fmax v17.4s, v17.4s ,v0.4s
                fmax v18.4s, v18.4s ,v0.4s
                fmax v19.4s, v19.4s ,v0.4s
                fmax v20.4s, v20.4s ,v0.4s
                fmax v21.4s, v21.4s ,v0.4s
                fmax v22.4s, v22.4s ,v0.4s
                fmax v23.4s, v23.4s ,v0.4s
                fmax v24.4s, v24.4s ,v0.4s
                fmax v25.4s, v25.4s ,v0.4s
                fmax v26.4s, v26.4s ,v0.4s
                fmax v27.4s, v27.4s ,v0.4s
                fmax v28.4s, v28.4s ,v0.4s
                fmax v29.4s, v29.4s ,v0.4s
                fmax v30.4s, v30.4s ,v0.4s
                fmax v31.4s, v31.4s ,v0.4s

                dup v1.4s, w9
                fmin v16.4s, v16.4s ,v1.4s
                fmin v17.4s, v17.4s ,v1.4s
                fmin v18.4s, v18.4s ,v1.4s
                fmin v19.4s, v19.4s ,v1.4s
                fmin v20.4s, v20.4s ,v1.4s
                fmin v21.4s, v21.4s ,v1.4s
                fmin v22.4s, v22.4s ,v1.4s
                fmin v23.4s, v23.4s ,v1.4s
                fmin v24.4s, v24.4s ,v1.4s
                fmin v25.4s, v25.4s ,v1.4s
                fmin v26.4s, v26.4s ,v1.4s
                fmin v27.4s, v27.4s ,v1.4s
                fmin v28.4s, v28.4s ,v1.4s
                fmin v29.4s, v29.4s ,v1.4s
                fmin v30.4s, v30.4s ,v1.4s
                fmin v31.4s, v31.4s ,v1.4s

            WriteStart:
                cmp x6, #1
                beq Write1
                cmp x6, #2
                beq Write2
                cmp x6, #3
                beq Write3
                b Write4
            Write1:
                b WriteEnd
            Write2:
                b WriteEnd
            Write3:
                b WriteEnd
            Write4:
                st1 {v16.4s}, [x11], x7
                st1 {v17.4s}, [x11], x7
                st1 {v18.4s}, [x11], x7
                st1 {v19.4s}, [x11], x7
                st1 {v20.4s}, [x11], x7
                st1 {v21.4s}, [x11], x7
                st1 {v22.4s}, [x11], x7
                st1 {v23.4s}, [x11], x7
                st1 {v24.4s}, [x11], x7
                st1 {v25.4s}, [x11], x7
                st1 {v26.4s}, [x11], x7
                st1 {v27.4s}, [x11], x7
                st1 {v28.4s}, [x11], x7
                st1 {v29.4s}, [x11], x7
                st1 {v30.4s}, [x11]
                st1 {v31.4s}, [x11]

        WriteEnd:

            subs x14, x14, #1
            add x0, x0, #8
            bne LoopKsize

        subs x6, x6, #4
        bgt LoopOc

    sub sp, sp, #128
    ld1 {v8.4s, v9.4s, v10.4s, v11.4s}, [sp], #64
    ld1 {v12.4s, v13.4s, v14.4s, v15.4s}, [sp], #64
    ret
#endif

